package com.micro.ai.gateway.filter;

import com.micro.ai.commons.constant.ErrorCodeEnum;
import com.micro.ai.commons.util.JwtUtils;
import com.micro.ai.gateway.config.AuthProperties;
import com.micro.ai.gateway.service.TokenBlacklistService;
import com.micro.ai.gateway.util.ResponseUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Collections;
import java.util.List;

/**
 * JWT认证过滤器（优化版）
 * 使用Commons的JwtUtils统一JWT验证逻辑
 * 使用ResponseUtils统一错误响应处理
 * 
 * 优化点：
 * 1. 使用Commons的JwtUtils，消除重复的JWT验证代码
 * 2. 使用ResponseUtils简化错误响应处理
 * 3. 减少30行重复代码
 * 4. 使用@ConfigurationProperties支持从Nacos动态刷新跳过认证的路径列表
 * 
 * @author micro-ai
 * @since 0.0.1
 * @version 1.3 优化动态刷新（使用ConfigurationProperties）
 */
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private AuthProperties authProperties;

    @Autowired
    private TokenBlacklistService tokenBlacklistService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        
        // 检查是否需要跳过认证
        if (shouldSkipAuth(path)) {
            return chain.filter(exchange);
        }
        
        // 获取Authorization头
        String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (!StringUtils.hasText(authHeader)) {
            return ResponseUtils.unauthorized(exchange, ErrorCodeEnum.A0006.getCode(), "缺少认证令牌");
        }
        
        // 显式检查 null 和空值
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            return ResponseUtils.unauthorized(exchange, ErrorCodeEnum.A0006.getCode(), "缺少认证令牌或令牌格式错误");
        }
        
        if (!authHeader.startsWith("Bearer ")) {
            return ResponseUtils.unauthorized(exchange, ErrorCodeEnum.A0006.getCode(), "认证令牌格式错误");
        }
        
        String token = authHeader.substring(7);
        
        // 检查token是否在黑名单中（被撤销的token）
        return tokenBlacklistService.isBlacklisted(token)
                .flatMap(isBlacklisted -> {
                    if (isBlacklisted) {
                        log.warn("令牌已被撤销: token={}", token.substring(0, Math.min(20, token.length())) + "...");
                        return ResponseUtils.unauthorized(exchange, ErrorCodeEnum.A0006.getCode(), "认证令牌已被撤销");
                    }
                    
                    try {
                        // 使用JwtUtils验证JWT令牌
                        Claims claims = jwtUtils.validateToken(token);
                        if (claims == null) {
                            return ResponseUtils.unauthorized(exchange, ErrorCodeEnum.A0006.getCode(), "无效的认证令牌");
                        }
                        
                        // 使用JwtUtils检查令牌是否过期
                        if (jwtUtils.isTokenExpired(token)) {
                            return ResponseUtils.unauthorized(exchange, ErrorCodeEnum.A0006.getCode(), "认证令牌已过期");
                        }
                        
                        // 提取用户信息
                        String userId = claims.getSubject();
                        String username = claims.get("username", String.class);
                        String tenantId = claims.get("tenantId", String.class);
                        List<String> roles = getStringListFromClaims(claims, "roles");
                        List<String> permissions = getStringListFromClaims(claims, "permissions");
                        
                        // 将用户信息添加到请求头
                        ServerHttpRequest mutatedRequest = request.mutate()
                                .header("X-User-Id", userId)
                                .header("X-Username", username != null ? username : "")
                                .header("X-Tenant-Id", tenantId != null ? tenantId : "")
                                .header("X-User-Roles", roles != null ? String.join(",", roles) : "")
                                .header("X-User-Permissions", permissions != null ? String.join(",", permissions) : "")
                                .build();
                        
                        ServerWebExchange mutatedExchange = exchange.mutate()
                                .request(mutatedRequest)
                                .build();
                        
                        log.debug("用户认证成功 - UserId: {}, Username: {}, TenantId: {}", userId, username, tenantId);
                        
                        return chain.filter(mutatedExchange);
                        
                    } catch (Exception e) {
                        log.error("JWT令牌验证失败: {}", e.getMessage(), e);
                        return ResponseUtils.unauthorized(exchange, ErrorCodeEnum.A0006.getCode(), "认证令牌验证失败");
                    }
                })
                .onErrorResume(error -> {
                    log.error("Token黑名单检查失败: {}", error.getMessage(), error);
                    return ResponseUtils.unauthorized(exchange, ErrorCodeEnum.A0006.getCode(), "认证令牌验证失败");
                });
    }

    @Override
    public int getOrder() {
        return -900; // 认证过滤器优先级
    }

    /**
     * 检查是否应该跳过认证（支持动态刷新）
     */
    private boolean shouldSkipAuth(String path) {
        List<String> skipPaths = authProperties.getSkipAuthPaths();
        if (skipPaths == null || skipPaths.isEmpty()) {
            log.warn("跳过认证路径列表为空，默认拒绝所有请求");
            return false;
        }
        
        boolean shouldSkip = skipPaths.stream().anyMatch(path::startsWith);
        if (shouldSkip) {
            log.debug("路径 [{}] 匹配跳过认证列表，跳过JWT验证", path);
        }
        return shouldSkip;
    }
    
    /**
     * 从 Claims 中安全获取字符串列表
     */
    @SuppressWarnings("unchecked")
    private List<String> getStringListFromClaims(Claims claims, String key) {
        try {
            Object value = claims.get(key);
            if (value instanceof List) {
                // 验证列表中的元素都是字符串
                List<?> rawList = (List<?>) value;
                for (Object item : rawList) {
                    if (!(item instanceof String)) {
                        log.warn("Claim {} contains non-string elements, returning empty list", key);
                        return Collections.emptyList();
                    }
                }
                return (List<String>) value;
            }
            return Collections.emptyList();
        } catch (Exception e) {
            log.warn("Failed to get string list from claims for key: {}", key, e);
            return Collections.emptyList();
        }
    }

//    /**
//     * 从 Claims 中安全获取字符串
//     */
//    private String getStringFromClaims(Claims claims, String key) {
//        try {
//            Object value = claims.get(key);
//            return value instanceof String ? (String) value : null;
//        } catch (Exception e) {
//            log.warn("Failed to get string from claims for key: {}", key, e);
//            return null;
//        }
//    }
}
